import { RegClass } from "../core/serialize";
import AppNode from "../core/app_node.ts";
import { EleDiv, EleInput, EleSpan } from "../core/data_ext.ts";
import data from "../core/cache_data.ts";
import { Protocol, ProtocolFactory, ProtocolS2CRegUser } from "../protocol_dist.ts";
import { Subject } from "../core/subject.ts";

@RegClass("SocketCtrl")
export default class SocketCtrl extends AppNode {
    socket: WebSocket = null;
    recv: EleDiv = null;
    lbState: EleSpan = null;
    ebUrl: EleInput = null;

    listenerMap: Map<new () => Protocol, Subject> = new Map();

    onLoad(): void {
        SocketCtrl.__ins = this;
        // "ws://local.gameptr.com:25555";
        this.ebUrl.value = data.storage.get("socket-ctrl-url", "");
    }
    onClickClean() {
        this.recv.innerText = "";
    }
    onClickLink() {
        if (this.socket) {
            return;
        }
        this.lbState.innerText = "connecting";
        data.storage.set("socket-ctrl-url", this.ebUrl.value);
        this.socket = new WebSocket(this.ebUrl.value);
        this.socket.onopen = (ev: any): any => {
            this.lbState.innerText = "connected";
        };
        this.socket.onmessage = (ev: MessageEvent) => {
            let msg: Protocol = null;
            try {
                msg = ProtocolFactory.CreateFromMixed(JSON.parse(ev.data));
            }
            catch (e) {
                console.warn("recv:", e);
            }

            if (msg) {
                console.log("recv:", msg.toMixed());
                this.recv.innerText += msg.toString();
                this.recv.innerText += "\n";
            }
            else {
                this.recv.innerText += ev.data;
                this.recv.innerText += "\n";
            }
            this.dispatch(msg);
        };
        this.socket.onclose = () => {
            this.lbState.innerText = "closed";
            this.socket = null;
        };
    }
    addListener<T extends Protocol>(msgCtor: new () => T, func: (msg: any, eventName: string) => void, master: any) {
        let subject = this.listenerMap.get(msgCtor);
        if (!subject) {
            subject = new Subject;
            this.listenerMap.set(msgCtor, subject);
        }
        subject.on("message", func, master);
    }
    delListener<T extends Protocol>(msgCtor: new () => T, func: (msg: any, eventName: string) => void, master: any) {
        let subject = this.listenerMap.get(msgCtor);
        if (!subject) {
            subject = new Subject;
            this.listenerMap.set(msgCtor, subject);
        }
        subject.off("message", func, master);
    }
    targetOff(master: any) {
        this.listenerMap.forEach(subject => {
            subject.targetOff(master);
        });
    }
    dispatch(msg) {
        let subjectList = this.listenerMap.get(msg.constructor);
        if (subjectList) {
            subjectList.emit("message", msg);
        }
    }
    onClickUnlink() {
        if (!this.socket) {
            return;
        }
        this.socket.close();
        this.socket = null;
    }

    sendMessage(msg: Protocol) {
        let mix = msg.toMixed();
        console.log("send:", mix);
        this.socket.send(JSON.stringify(mix));
    }

    private static __ins: SocketCtrl = null;
    static get ins() {
        return this.__ins;
    }
};